/*
 * Copyright (C) 2010-2011 Comcast Cable.  All Rights Reserved.
 */
package com.hercules.android.receiver.painting;

import com.hercules.android.receiver.AndroidReceiver;
import com.hercules.android.receiver.LogUtil;
import com.hercules.android.receiver.model.commands.XRECallCommand;
import com.hercules.android.receiver.model.commands.XRECommand;
import com.hercules.android.receiver.model.commands.XREConnectCommand;
import com.hercules.android.receiver.model.commands.XREDeleteCommand;
import com.hercules.android.receiver.model.commands.XRENewCommand;
import com.hercules.android.receiver.model.commands.XRERestartCommand;
import com.hercules.android.receiver.model.commands.XREShutdownCommand;
import com.hercules.android.receiver.model.objects.XREView;

import java.util.logging.Logger;

/**
 * <p>
 * This is the abstract base painter class in this component. It exposes several template methods to allow children to
 * behave differently based on different command types, including NEW, DELETE, addChild CALL and removeChild CALL.
 * </p>
 * <p>
 * AndroidAssembly1 refactored
 * <p>
 * <b> Usage: </b>
 *
 * <pre>
 * // This component is expected to be plugged into XREObjectCommandHandler as a map from XRE resource class names
 * // to the XREObjectPainter instances. This can be done by:
 * Map&lt;String, XREObjectPainter&gt; objectPainters = XREObjectPainterUtils.getDefaultPaintersMap();
 *
 * // Then when an incoming command arrives, command handlers will first get the XRE view object:
 * XREView view = (XREView) receiver.getApplicationData().getElementsXREIdToObject().get(targetId);
 *
 * // After this, the painter will be retrieved from object painter map:
 * XREObjectPainter painter = objectPainters.get(view.getClass().getSimpleName());
 *
 * // Then the painter will be called to paint the view.
 * // For example, connect command:
 * XREConnectCommand command = new XREConnectCommand();
 * painter.paintView(receiver, view, command);
 * // CONNECT commands will initialize the painter context.
 *
 * // Different command type will result in different painting logic, but the usage is all identical
 * </pre>
 *
 * </p>
 * <p>
 * <b> Thread safety: </b> This class is not thread safe since it is mutable.
 * </p>
 *
 * @author linwe, EveningSun
 * @author 7realm
 * @version 1.0
 */
public abstract class XREObjectPainter {
    /**
     * <p>
     * Represents the logger. Can be any value, accessed by getter and setter.
     * </p>
     */
    private Logger logger;

    /**
     * <p>
     * Constructor. Logger should be initialized.
     * </p>
     */
    protected XREObjectPainter() {
        logger = LogUtil.getLogger(this.getClass().getSimpleName());
    }

    /**
     * <p>
     * Paint the specified view.
     * </p>
     *
     * @param receiver the AndroidReceiver instance
     * @param view the XREView to paint
     * @param command the command triggering the repaint
     * @throws IllegalArgumentException if any argument is null
     * @throws XREObjectPainterException if any error occurs while painting the view
     */
    public void paintView(AndroidReceiver receiver, XREView view, XRECommand command) throws XREObjectPainterException {
        String methodName = "paintView";
        LogUtil.enter(logger, this, methodName, receiver, view, command);

        try {
            PaintersUtil.checkNull(receiver, "receiver");
            PaintersUtil.checkNull(view, "view");
            PaintersUtil.checkNull(command, "command");

            if (command instanceof XREConnectCommand) {
                XREObjectPainterUtils.initialize(receiver);
            } else if (command instanceof XREShutdownCommand) {
                XREObjectPainterUtils.finalize(receiver);
            } else if (command instanceof XRERestartCommand) {
                XREObjectPainterUtils.finalize(receiver);
                XREObjectPainterUtils.initialize(receiver);
            } else if (command instanceof XRENewCommand && ((XRENewCommand) command).getId() == view.getId()) {
                paintViewOnNew(receiver, view, command);
            } else if (command instanceof XREDeleteCommand
                && ((XREDeleteCommand) command).getTargetId() == view.getId()) {
                paintViewOnDelete(receiver, view, command);
            } else if (command instanceof XRECallCommand && "addChild".equals(((XRECallCommand) command).getMethod())
                && ((XRECallCommand) command).getTargetId() == view.getId()) {
                paintViewOnAddChild(receiver, view, command);
            } else if (command instanceof XRECallCommand
                && "removeChild".equals(((XRECallCommand) command).getMethod())
                && ((XRECallCommand) command).getTargetId() == view.getId()) {
                paintViewOnRemoveChild(receiver, view, command);
            } else if (command instanceof XRECallCommand
                && "setChildIndex".equals(((XRECallCommand) command).getMethod())
                && ((XRECallCommand) command).getTargetId() == view.getId()) {
                paintViewOnSetChildIndex(receiver, view, command);
            } else {
                paintViewOnUpdate(receiver, view, command);
            }
        } catch (IllegalArgumentException e) {
            throw LogUtil.error(getLogger(), this, methodName, e);
        } catch (XREObjectPainterException e) {
            throw LogUtil.error(getLogger(), this, methodName, e);
        }

        LogUtil.exit(getLogger(), this, methodName);
    }

    /**
     * <p>
     * Paint the specified view when the view is created with NEW command. This method is expected to be called by
     * paintView method.
     * </p>
     *
     * @param receiver the AndroidReceiver instance
     * @param view the XREView to paint
     * @param command the command triggering the repaint
     * @throws IllegalArgumentException if any argument is null
     * @throws XREObjectPainterException if any other error occurs
     */
    protected abstract void paintViewOnNew(AndroidReceiver receiver, XREView view, XRECommand command)
        throws XREObjectPainterException;

    /**
     * <p>
     * Paint the specified view when the view is deleted with DELETE command. This method is expected to be called by
     * paintView method.
     * </p>
     *
     * @param receiver the AndroidReceiver instance
     * @param view the XREView to paint
     * @param command the command triggering the repaint
     * @throws IllegalArgumentException if any argument is null
     * @throws XREObjectPainterException if any other error occurs
     */
    protected abstract void paintViewOnDelete(AndroidReceiver receiver, XREView view, XRECommand command)
        throws XREObjectPainterException;

    /**
     * <p>
     * Paint the specified view when the view hierarchy is not changed by the incoming command. This method is expected
     * to be called by paintView method.
     * </p>
     *
     * @param receiver the AndroidReceiver instance
     * @param view the XREView to paint
     * @param command the command triggering the repaint
     * @throws IllegalArgumentException if any argument is null
     * @throws XREObjectPainterException if any other error occurs
     */
    protected abstract void paintViewOnUpdate(AndroidReceiver receiver, XREView view, XRECommand command)
        throws XREObjectPainterException;

    /**
     * <p>
     * Paint the specified view when a child is added to the view with CALL command. This method is expected to be
     * called by paintView method.
     * </p>
     *
     * @param receiver the AndroidReceiver instance
     * @param view the XREView to paint
     * @param command the command triggering the repaint
     * @throws IllegalArgumentException if any argument is null
     * @throws XREObjectPainterException if any other error occurs
     */
    protected abstract void paintViewOnAddChild(AndroidReceiver receiver, XREView view, XRECommand command)
        throws XREObjectPainterException;

    /**
     * <p>
     * Paint the specified view when a child is removed to the view with CALL command. This method is expected to be
     * called by paintView method.
     * </p>
     *
     * @param receiver the AndroidReceiver instance
     * @param view the XREView to paint
     * @param command the command triggering the repaint
     * @throws IllegalArgumentException if any argument is null
     * @throws XREObjectPainterException if any other error occurs
     */
    protected abstract void paintViewOnRemoveChild(AndroidReceiver receiver, XREView view, XRECommand command)
        throws XREObjectPainterException;

    /**
     * <p>
     * Paint the specified view when a child index is set with CALL command. This method is expected to be called by
     * paintView method.
     * </p>
     * <p>
     * AndroidAssembly2 new method
     *
     * @param receiver the AndroidReceiver instance
     * @param view the XREView to paint
     * @param command the command triggering the repaint
     * @throws IllegalArgumentException if any argument is null
     * @throws XREObjectPainterException if any other error occurs
     */
    protected abstract void paintViewOnSetChildIndex(AndroidReceiver receiver, XREView view, XRECommand command)
        throws XREObjectPainterException;

    /**
     * <p>
     * Protected getter for the logger that this class used to log information.
     * </p>
     *
     * @return the logger
     */
    protected Logger getLogger() {
        return this.logger;
    }

    /**
     * <p>
     * Set the logger that this class used to log information.
     * </p>
     *
     * @param logger the logger to set
     */
    public void setLogger(Logger logger) {
        this.logger = logger;
    }
}
